/*
 * basic_connection application showing the basic generic usage of the dbConnect API
 * Copyright (C) 2002 Johnathan Ingram, jingram@rogueware.org
 *
 * This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public
 *   License as published by the Free Software Foundation; either
 *   version 2.1 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  US
 *
 */


#include <iostream>
#include <map>
#include <string>

#include <time.h>
#include <stdio.h>

#include "dbconn/dbconnect.h"
#include "dbconn/simpleThreads.h"

using namespace std;


char *tf[] = {"false", "true"};


void 
showTableData(
      DbQueryVar &conn)
   throw(
      BaseException)
{
   // Execute the same query on both connections
   string sqlQuery =
      "SELECT "
      "  id, description "
      "FROM "
      "  TypeTest ";
   conn->command(sqlQuery);
   conn->execute();
   
   
   // List the contents of the table
   cout << "TypeTest table data: " << endl;
   cout << "----------------------------------------- " << endl;
   cout << "id\tdescription" << endl;
   cout << "--\t-----------" << endl;
   if (conn->eof())
      cout << "No rows in result set for this query" << endl;
   else
   {
      while (!conn->eof())
      {
         conn->fetchNext();
         cout << conn->getFieldByName("id")->asString() << "\t" << conn->getFieldByName("description")->asString() << endl;
      }
   }
   cout << endl;   
}


int 
main(
      int argc, char** argv)
{   
   char sql[2048];
   map<string, DbConnection::Driver> drivers;

   drivers["MYSQL"] =       DbConnection::MYSQL;
   drivers["MSQL"] =        DbConnection::MSQL;
   drivers["POSTGRESQL"] =  DbConnection::POSTGRESQL;
   drivers["DB2"] =         DbConnection::DB2;

   if (argc == 2)
   {
      // Use smart pointers. Must be declared outside of the try
      //   as if an exception is caught it will loose scope and free. 
      DbConnectionVar driver;
      DbQueryVar      conn;

      try
      {
         driver = new DbConnection(drivers[argv[1]]);
         // Print out the driver information.
         DbConnectionDriverInfo* info = driver->getDriverInformation();
         cout << "Author        : " << info->author << endl;
         cout << "Vendor        : " << info->vendor << endl;
         cout << "Copyright     : " << info->copyright << endl;
         cout << "Driver Type   : " << info->driverType << endl;
         cout << "Driver Name   : " << info->driverName << endl;
         cout << "Description   : " << info->driverDescription << endl;
         cout << "DbConnect Ver : " << info->dbConnectVersion << endl;
         cout << endl;

         // Connect to the database. (DB2 takes different conn string)
         if (drivers[argv[1]] == DbConnection::DB2)
            driver->connect("dbconn", "letmein", "dbConnDB", "", 5, 2);
         else
            driver->connect("dbconnect", "letmein", "dbConnectDB", "localhost", 5, 2);
         
         // Get a query connection object
         conn = driver->requestQueryConnection();

         // Set the ping interval on the connection to 1 hour 
         // Note: This is defaulted to 5 min. So in the event that a connection
         //         becomes stale, you will receeve errors on the connection for 5 min
         //         until it is pinged and a new connection to the server established.
         //       If 5 min is too long, then set it to an acceptable ping interval. 
         //         The more the connection is pinged, the more overhead is introduced.
         //       The minimum is can be set to is 10 seconds
         driver->setPingInterval(1 * 60 * 60);
         
         
         // Test 1: Connection by doing a query on the TypeTest table         
         cout << "Test 1: Querying on connection. " << endl;
         showTableData(conn);
         cout << endl << endl;
         
         
         // Test 2: Stop the database to make the existing connection stale
         cout << "Test 2: Stop and start the database this application is connected to. " << endl;
         // Sleep for 10 seconds to make sure the ping interval is exceeded
         SimpleThread::sleep(10000);

         cout << "Press any key to continue..." << endl;
         getchar();
         cout << endl << endl;
                  
         
         // Test 3: Do the query again but with the ping interval at one hour forcing an error
         cout << "Test 3: Doing query on 'stale' connection. Ping Interval 1 hour" << endl;
         cout << "        This will generate an error as the ping interval has not been exceeded" << endl;
         try
         {
            showTableData(conn);
            cout << "You did not stop / start the database or took more that 1 hour to do so ????" << endl;
            exit(1);
         }
         catch(BaseException &ex)
         {
            cout << "Expected Error trapped: " << ex.name << "  " << ex.code << " : " << ex.description << endl;
         }
         cout << endl << endl;
         


         // Test 4: Do the query again to make sure the ping establishes a new connection
         cout << "Test 4: Doing query on 'stale' connection. Ping interval 10 seconds" << endl;
         cout << "        This will automatically connect as the ping interval has exceeded..." << endl;

         // Set the ping interval on the connection to 10 seconds
         driver->setPingInterval(10);
         
         showTableData(conn);
         cout << "The reconnection test completed OK" << endl;
         cout << endl << endl;
      }
      catch(BaseException &ex)
      {
         // Only need to catch a single exception. Can use name etc to determine the actual exception
         cout << "DbConnect Exception: " << ex.name << endl 
              << "  " << ex.code << " : " << ex.description << endl;
      }
      catch(...)
      {
         cout << "An Unknown exception has been trapped!\n" << endl;
      }
      cout << endl;
        
      // The 'conn' and 'driver' objects are smart pointers and will cleanup as soon as they go out of scope      
   }
   else
   {
      cout << "Syntax: basic_connection DRIVER" << endl;
      cout << "Drivers: MYSQL" << endl;
      cout << "       : MSQL" << endl;
      cout << "       : POSTGRESQL" << endl;
      cout << "       : DB2" << endl;
      return 1;
   }
   
   return 0;
}
